%{
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #include "fonctions.h"
    
    extern FILE *yyin;
   
    extern int yylex();
    extern int yyerror(char *s);
    extern int ligne;



    s_expr opBinEntier(s_expr expr1,s_expr expr2,int op);
    s_expr opBinBool(s_expr expr1,s_expr expr2,int op);
    void error(char*);
    void errorAndQuit(char *s);
%}
%start program;

%union { char* id; int val; char* vals; int type; void* list; struct symbole* symbole; struct expr expr; int nextquad; struct instr next; struct n n;}

%token _program _var _type _array _of _range _function _ref _if _then _else _while _do _affect _return _begin _end _read _write _writeln _gt _lt _ne _and _or _xor _not _cst_bool _cst_int _cst_string _ident;

%type <id> _ident
%type <symbole> lvalue 
%type <expr> expr cst
%type <val> _cst_int _cst_bool exprlist
%type <vals> _cst_string
%type <type> typename _type
%type <list> identlist
%type <nextquad> M
%type <next> instr sequence
%type <n> N

%left _not
%left _or _xor 
%left _and
%left '<' _gt _lt '=' _ne '>'
%left '+' '-' 
%left '*' '/'
%left '^'

%nonassoc _not_else
%nonassoc _else

%%

program : _program _ident vardecllist fundecllist instr {complete($5.next,quads->last->q->index+1);};

vardecllist : {/*Rien*/}
            | varsdecl {/*Rien*/}
            | varsdecl ';' vardecllist {/*Rien*/}
            ;
varsdecl : _var identlist ':' typename {ajouterListeSymbole($2,$4,1);};
identlist : _ident {$$=rajouterList(NULL,$1);}
          | _ident ',' identlist { $$ = rajouterList($3,$1);}
          ;
typename : _type { $$ = $1; }
			;
fundecllist : //Fonctions
            | fundecl ';' fundecllist
            ;
fundecl : _function _ident {fct_en_cours = newFct(strdup($2));} '(' arglist ')' ':' _type { fct_en_cours->type_retour = $8; } vardecllist instr {supprimerTable_locale(fct_en_cours, 1); fct_en_cours = NULL;}
        ;
arglist : 
        | arg //TODO
        | arg ',' arglist //TODO
        ;
arg : _ident ':' typename {ajouterListeSymbole(rajouterList(NULL,$1) ,$3, 0);}
    | _ref _ident ':' typename {free($2);/*TODO ou pas*/ }
    ;
instr : _if expr _then M instr %prec _not_else {
                                    complete($2.true,$4);
                                    $$.next = concat($2.false,$5.next);} 
      | _if expr _then M instr  _else N instr { 
                    complete($2.true, $4);
                    complete($2.false, $7.quad);
                    $$.next = concat($5.next, concat($7.next,$8.next));
                    }
      | _while M expr _do M instr {
                                genGoto(GOTO, $2);
                                complete($3.true, $5);
                                complete($6.next, $2);
                                $$.next = $3.false;
                                }
      | lvalue _affect expr {if ($3.symbole != NULL) { //int
                                gen(AFFECT,$3.symbole,NULL,$1);freeAlltmp();
                            }
                            else //booleen
                            {
                                genCST(CST, 1, $1);
                                complete($3.true, quads->last->q->index);
                                genGoto(GOTO, quads->last->q->index + 3);
                                genCST(CST, 0, $1);
                                complete($3.false, quads->last->q->index);
                                freeAlltmp();
                            }
                            $$.next = NULL;
                            }
      | _return expr {
						genAffectResgistre(AFFECT_REGISTRE, $2.symbole, 0); //TODO gérer les booléens
						supprimerTable_locale(fct_en_cours, 0);
						gen(RETURN, NULL, NULL, NULL);$$.next = NULL;}
      | _return { supprimerTable_locale(fct_en_cours, 0);gen(RETURN, NULL, NULL, NULL);$$.next = NULL;} 
      | _ident '(' exprlist ')' {
									genAppelFonction(APPELFONCTION, place($1, 1)->fonction);
									$$.next = NULL;}
      | _ident '(' ')' {genAppelFonction(APPELFONCTION, place($1, 1)->fonction);$$.next = NULL;}
      | _begin sequence _end { $$.next = $2.next;}
      | _begin _end { $$.next = NULL;}
      | _read lvalue {printf("read lvalue;\n");gen(READ,$2,NULL,NULL);$$.next = NULL;}
      | _write lvalue {printf("write lvalue\n");
                        if ($2->type == TYPE_INT) { //int
                            gen(WRITE_INT,$2,NULL,NULL);freeAlltmp();
                        } else
                        {
                            genIfGoto(IFGOTO, $2, quads->last->q->index+4);
                            genString(WRITE_STRING, 1);
                            genGoto(GOTO, quads->last->q->index + 3);
                            genString(WRITE_STRING, 0);
                        }
                        $$.next = NULL;
                    }
      | _write cst {printf("write cst int\n");
                    if ($2.symbole != NULL) { //int
                        gen(WRITE_CST_INT,$2.symbole,NULL,NULL);
                    }else
                    {
                        genString(WRITE_STRING, 0);
                        complete($2.true, quads->last->q->index);
                        genGoto(GOTO, quads->last->q->index + 3);
                        genString(WRITE_STRING, 1);
                        complete($2.false, quads->last->q->index);
                    }
                    freeAlltmp();
                    $$.next = NULL;
                    }
      | _write _cst_string { printf("write string\n");genString(WRITE_STRING,ajouterString($2)); freeAlltmp();$$.next = NULL;}
      | _writeln lvalue {printf("writeln lvalue\n");
                        if ($2->type == TYPE_INT) { //int
                            gen(WRITELN_INT,$2,NULL,NULL);freeAlltmp();
                        } else
                        {
                            genIfGoto(IFGOTO, $2, quads->last->q->index+4);
                            genString(WRITELN_STRING, 1);
                            genGoto(GOTO, quads->last->q->index + 3);
                            genString(WRITELN_STRING, 0);
                        }
                        $$.next = NULL;
                        }
      | _writeln cst {
          printf("writeln cst int\n");
                    if ($2.symbole != NULL) { //int
                        gen(WRITELN_CST_INT,$2.symbole,NULL,NULL);
                    }else
                    {
                        genString(WRITELN_STRING, 0);
                        complete($2.true, quads->last->q->index);
                        genGoto(GOTO, quads->last->q->index + 3);
                        genString(WRITELN_STRING, 1);
                        complete($2.false, quads->last->q->index);
                    }
                    freeAlltmp();
                    $$.next = NULL;
                }
      | _writeln _cst_string { printf("writeln string\n");genString(WRITELN_STRING,ajouterString($2));freeAlltmp(); $$.next = NULL;}
      ;
cst : _cst_bool {$$.symbole=NULL;
                if ($1 == 0) //false
                {
                    $$.false = ajouterALaListeDeQuad(NULL,gen(GOTO,NULL,NULL,NULL), 0);
                    $$.true = NULL;
                } else
                {
                    $$.true = ajouterALaListeDeQuad(NULL,gen(GOTO,NULL,NULL,NULL), 0);
                    $$.false = NULL;
                }
               }
    | _cst_int {$$.symbole=newtemp();$$.symbole->type=TYPE_INT;genCST(CST,$1,$$.symbole);}
    ;
sequence : instr ';' M sequence {
                                $$.next = $4.next;
                                if ($1.next != NULL)
                                {
                                    complete($1.next,$3);
                                }
                                } 
         | instr ';' {$$.next = $1.next; }                    
         | instr  {$$.next = $1.next; }
         ;
lvalue : _ident {$$=place($1, 1);}
       ;
exprlist : expr { gen(ALLOC,NULL,NULL,NULL);deplacerOffset(tab_symb,4);gen(PLACER_ARG, $1.symbole, NULL, NULL); $$ = 1;}
         | expr ',' exprlist { gen(ALLOC,NULL,NULL,NULL);;deplacerOffset(tab_symb,4);gen(PLACER_ARG, $1.symbole, NULL, NULL); $$ = $3 + 1;} 
         ;
expr : cst {$$=$1;}
     | '(' expr ')' {$$=$2;}
     | expr '+' expr {$$=opBinEntier($1,$3,OP_ADD);}
     | expr '-' expr {$$=opBinEntier($1,$3,OP_SUB);}
     | '-' expr {
                if ($2.symbole != NULL && $2.symbole->type == TYPE_INT)
                {
                    $$.symbole = newtemp();
                    gen(OP_SUB,NULL,$2.symbole,$$.symbole);
                }
                else
                {
                    error("Types incompatibles : entier demandé");
                }   
                }
     | expr '*' expr {$$=opBinEntier($1,$3,OP_MUL);}
     | expr '/' expr {$$=opBinEntier($1,$3,OP_DIV);}
     | expr '^' expr 
     | expr '<' expr {$$=opBinBool($1,$3,COMP_INF);} 
     | expr _gt expr {$$=opBinBool($1,$3,COMP_SUP_EGAL);}
     | expr '>' expr {$$=opBinBool($1,$3,COMP_SUP);}
     | expr _lt expr {$$=opBinBool($1,$3,COMP_INF_EGAL);}
     | expr '=' expr {$$=opBinBool($1,$3,COMP_EGAL);}
     | expr _ne expr {$$=opBinBool($1,$3,COMP_NOT_EGAL);}
     | expr _and M expr {if (verifType(TYPE_BOOL, $1, $4))
                         {
                            complete($1.true,$3);
                            $$.true = $4.true;
                            $$.false = concat($1.false, $4.false);
                         }}
     | expr _or M expr {if (verifType(TYPE_BOOL, $1, $4))
                        {complete($1.false,$3);
                        $$.false = $4.false;
                        $$.true = concat($1.true, $4.true);}}
     | expr _xor {if ($1.symbole != NULL) {error("Types incompatibles : booleen demande");}
                 //Je stocke la première partie de l'expression
                 s_symbole* s = newtemp(); 
                 $1.symbole = s;
                 s->name = "_xor";
                 int apres = quads->last->q->index + 7;
                 gen(ALLOC,NULL,NULL,s);
                 genCST(CST,1,s); 
                 genGoto(GOTO,apres);
                 gen(ALLOC,NULL,NULL,s);
                 genCST(CST,0,s);
                 genGoto(GOTO,apres);
                 complete($1.true,quads->last->q->index-5);
                 complete($1.false,quads->last->q->index-2);
                } expr 
                {
                s_symbole* s = $1.symbole;
                $1.symbole = NULL;
                if (verifType(TYPE_BOOL,$1,$4))
                {
                                genAffectResgistre(AFFECT_REGISTRE, s, 0);
                                genFREE(FREE, 4);
                                $$.true = ajouterALaListeDeQuad(NULL,gen(IFGOTO, NULL,NULL,NULL), 0);
                                $$.false = ajouterALaListeDeQuad(NULL,gen(GOTO,NULL,NULL,NULL), 0);
                                complete($4.true, quads->last->q->index-3);
                                genAffectResgistre(AFFECT_REGISTRE, s, 0);
                                genFREE(FREE, 4);
                                $$.false = ajouterALaListeDeQuad($$.false,gen(IFGOTO, NULL,NULL,NULL), 0);
                                $$.true = ajouterALaListeDeQuad($$.true ,gen(GOTO,NULL,NULL,NULL), 0);
                                complete($4.false, quads->last->q->index-3);
                                $$.symbole = NULL;
                                freeAlltmp();
                                supprimerSymbole(s);
                            }
                            else
                            {
                                error("Types incompatibles : booleen demande");
                            }
                }
     | _not expr { if ($2.symbole == NULL){ $$.true = $2.false; $$.false = $2.true; $$.symbole = NULL;}}
     | _ident '(' exprlist ')' {genAppelFonction(APPELFONCTION, place($1, 1)->fonction);$$.symbole = newtemp();
						genStockResgistre(STOCK_REGISTRE, $$.symbole, 0);}
     | _ident '(' ')' {
						genAppelFonction(APPELFONCTION, place($1, 1)->fonction);
						$$.symbole = newtemp();
						genStockResgistre(STOCK_REGISTRE, $$.symbole, 0);
						}
     | _ident {$$.symbole=place($1,1);
                if ($$.symbole == NULL)
                {   // Dans le cas de l'utilisation d'un identifiant non déclaré
                    char *e = malloc(sizeof(char)*100);
                    memset(e,'\0',100);
                    sprintf(e,"Variable '%s' non déclaré.\n",$1);
                    error(e);
                }
                if ($$.symbole->type == TYPE_BOOL)
                {
                    $$.false = ajouterALaListeDeQuad(NULL,gen(IFGOTO, $$.symbole, NULL, NULL), 0);
                    $$.true = ajouterALaListeDeQuad(NULL,gen(GOTO, NULL, NULL, NULL), 0);
                    $$.symbole = NULL;
                }
            }
     ;
M : {$$=quads->last->q->index+1;}
    ;
N : {$$.next = ajouterALaListeDeQuad(NULL,gen(GOTO, NULL, NULL, NULL), 0); 
     $$.quad=quads->last->q->index+1;}
    ;

%%

int yyerror(char *s)
{
//TODO en cas d'erreur
    printf("ERREUR : ligne %d %s\n",ligne,s);
    exit(-1);
}

void error(char *s)
{
    printf("ERREUR : ligne %d : %s\n\n",ligne,s);
    printf("ECHEC : la compilation du fichier a échoué avec succès :)\n");
    exit(-1);
}

s_expr opBinEntier(s_expr expr1,s_expr expr2,int op)
{
    s_expr res;
    if (verifType(TYPE_INT, expr1, expr2))
    {
        res.symbole = newtemp();
        gen(op,expr1.symbole,expr2.symbole,res.symbole);
    }
    else
    {
        error("Types incompatibles : entier demandé");
    }
    return res;
}

s_expr opBinBool(s_expr expr1,s_expr expr2,int op)
{
    s_expr res;
    if (verifType(TYPE_INT, expr1, expr2))
    {
        genAffectResgistre(AFFECT_REGISTRE,expr1.symbole,1);
        genAffectResgistre(AFFECT_REGISTRE,expr2.symbole,2);
        freeAlltmp();
        res.true = ajouterALaListeDeQuad(NULL,gen(op,NULL,NULL,NULL), 0);
        res.false = ajouterALaListeDeQuad(NULL,gen(GOTO,NULL,NULL,NULL), 0);
        res.symbole = NULL;
    }
    else
    {
        error("Types incompatibles : entier demandé");
    }
    return res;
}
 
int main(int argc, char** argv)
{
    printf("\nTIASC : This Is A Scalpa Compiler\n");
    printf("Compilateur pour le langage Scalpa par Marie-Hélène GODDET et Damien FINCK\n\n");
    if (argc != 2)
    {   // Necessite en argument le fichier source
        printf("\nUsage : %s <fichier_source.scalpa>\n\n",argv[0]);
        exit(-1);
    }
    else
    {
        printf("\n");
        if (argc==2)
        {
            printf("Analyse du fichier : %s\n",argv[1]);
            yyin = fopen(argv[1],"r");
            if (yyin == NULL)
            {   //Fichier non trouvé
                printf("ERREUR : Le fichier <%s> n'a pas été trouvé\n",argv[1]);
                printf("ERREUR : Fin du programme.\n");
                return -1;
            }
        }
        
        //Ajout de true et false dans les strings
        ajouterString(strdup("\"false\""));
        ajouterString(strdup("\"true\""));
        
        fct_en_cours = NULL;
        indexFonction = 0;
        
        // Analyse du fichier source
        printf("Début de l'analyse du fichier\n");
        yyparse();
        printf("Fin de l'analyse\n");
        
        
        fclose(yyin); // fermeture du fichier source
        
        // Génération du fichier de sortie en Assembleur MIPS
        ecrireCode(outFile(argv[1]));
        
        
        
        ecrireTableSymbole2();//TODO: juste pour tester
    }
    return 0;
}
